﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cofe.Core.EntryLinks;
using Cofe.Core.Interfaces;
using Cofe.Core.Property;
using Cofe.Core.Service;

namespace Cofe.Core.Implements
{
    /// <summary>
    /// Store EntryLink data in a flat Dictionary[parentParsePath, List[Tuple[Label, TargetPath]]].
    /// </summary>
    public class CoreEntryLinkContainerBehavior : IContainerBehavior
    {
        #region Constructor

      
        [Export(typeof(ICofeService))]
        [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFE)]
        public class SecondaryPPFactory : ISecondaryPropertyProviderFactory
        {
            public IEnumerable<IPropertyProvider> GetSecoondaryPropertyProviders(IPropertyHost propertyHost)
            {
                if (!(propertyHost.AllPropertyProviders.Any(pp => pp is IEntryLinkSecondaryPP)))
                {
                    string parseName = propertyHost.Behaviors.GetProperty<string>(CofeProperties.FullParseName);
                    yield return new EntryLinkSecondaryPP(new CoreEntryLinkContainerBehavior(parseName));
                }
            }
            public IServiceLocater ServiceLocater { get; set; }  
        }

        public CoreEntryLinkContainerBehavior(string forPath)
        {
            this.ParsePath = forPath;
           
        }

        #endregion

        #region Methods

        private List<Tuple<string, string>> getEntryListForPath(string path, bool createNew)
        {
            lock (_entryLinkDic)
            {
                if (!(_entryLinkDic.ContainsKey(path)))
                    if (createNew)
                        _entryLinkDic.Add(path, new List<Tuple<string, string>>());
                    else return null;
                return _entryLinkDic[path];
            }
        }

        public IPropertyHost Add(int position, string label, string targetPath, ParsableType parsableType)
        {
            var SubLinks = getEntryListForPath(this.ParsePath, true);
            if (position == -1)
                position = SubLinks.Count;

            if (position >= SubLinks.Count)
                position = SubLinks.Count;

            if (parsableType != ParsableType.ParsePath && parsableType != ParsableType.ParsePathLookup)
            {
                var targetEntry = CofeServices.PathParserManager.Behaviors.TryParsePath(targetPath, parsableType);
                if (targetEntry == null)
                    throw new System.IO.FileNotFoundException(targetPath);
                targetPath = targetEntry.Behaviors.GetProperty<string>(CofeProperties.FullParseName);
            }

            string finalLabel = label;
            int ctr = 1;
            while (SubLinks.Any(tup => tup.Item1.Equals(finalLabel)))
                finalLabel = label + ctr++.ToString();

            SubLinks.Insert(position, new Tuple<string, string>(finalLabel, targetPath));

            return GetItem(finalLabel);
        }

        public IEnumerable<string> GetLabels()
        {
            var SubLinks = getEntryListForPath(this.ParsePath, false);
            if (SubLinks == null)
                return new List<string>();
                            
            return SubLinks.Select(tup => tup.Item1).ToArray();
        }

        public IPropertyHost GetItem(string label)
        {
            var SubLinks = getEntryListForPath(this.ParsePath, false);
            if (SubLinks == null)
                return null;
            
            var foundLink = SubLinks.FirstOrDefault(tup => tup.Item1 == label);
            if (foundLink == null)
                return null;
            var pp = new EntryLinkPrimaryPP(this, ParsePath, label, foundLink.Item2);
            return CofeServices.EntryConstructor.ConstructPropertyHost(pp);
        }

        public void Remove(string label)
        {
            var SubLinks = getEntryListForPath(this.ParsePath, false);
            if (SubLinks == null)
                return;

            var foundLink = SubLinks.FirstOrDefault(tup => tup.Item1 == label);
            if (foundLink != null)
                SubLinks.Remove(foundLink);
        }

        public int GetPosition(string label)
        {
            var SubLinks = getEntryListForPath(this.ParsePath, false);
            if (SubLinks == null)
                return -1;

            var foundLink = SubLinks.FirstOrDefault(tup => tup.Item1 == label);
            if (foundLink == null)
                return -1;
            return SubLinks.IndexOf(foundLink);
        }

        public void SetPosition(string label, int position)
        {
            var SubLinks = getEntryListForPath(this.ParsePath, false);
            if (SubLinks == null)
                return;

            var foundLink = SubLinks.FirstOrDefault(tup => tup.Item1 == label);
            if (foundLink != null)
            {
                SubLinks.Remove(foundLink);

                if (position >= SubLinks.Count)
                    position = SubLinks.Count - 1;

                SubLinks.Insert(position, foundLink);
            }
        }

        public void Rename(string label, string newLabel)
        {
            var SubLinks = getEntryListForPath(this.ParsePath, false);
            if (SubLinks == null)
                return;

            var foundLink = SubLinks.FirstOrDefault(tup => tup.Item1 == label);
            var newLink = new Tuple<string, string>(newLabel, foundLink.Item2);
            if (foundLink != null)
            {
                var position = SubLinks.IndexOf(foundLink);
                SubLinks.Remove(foundLink);
                SubLinks.Insert(position, newLink);
            }
        }

        #endregion

        #region Data

        //Dictionary[parentParsePath, List[Tuple[Label, TargetPath]]].
        public static Dictionary<string, List<Tuple<string, string>>> _entryLinkDic =
            new Dictionary<string, List<Tuple<string, string>>>();

        #endregion

        #region Public Properties

        public string ParsePath { get; private set; }

        #endregion

    }
}
